package com.summerbird.mallchat.common.chat.service.strategy.frequency;

import com.summerbird.mallchat.common.common.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * @description: 滑动窗口限流算法实现：ZSet实现
 * @author SummerBird
 * @date 2024/1/30 18:53
 * @version 1.0
 */
@Component
@Slf4j
public class SlidingWindowFrequencyStrategy extends AbstractFrequencyControlStrategy <FrequencyControlDTO>{

    public static final int STEP = 10;

    @Override
    protected boolean reachRateLimit(Map<String, FrequencyControlDTO> frequencyControlMap) {
        // 先删除过期的时间
        deleteExpireTime(frequencyControlMap);
        ArrayList<String> keyList = new ArrayList<>(frequencyControlMap.keySet());
        for (int i = 0; i < keyList.size(); i++) {
            String key = keyList.get(i);
            // 该窗口内是否限流
            Integer count = Optional.ofNullable(RedisUtils.zCard(key)).map(Long::intValue).orElse(null);
            FrequencyControlDTO frequencyControlDTO = frequencyControlMap.get(key);
            if(Objects.nonNull(count) && count >= frequencyControlDTO.count){
                //频率超过了
                log.warn("frequencyControl limit key:{},count:{}", key, count);
                return true;
            }
        }
        return false;
    }

    // 删除过期的时间
    private void deleteExpireTime(Map<String, FrequencyControlDTO> frequencyControlMap) {
        frequencyControlMap.forEach((k,v) -> {
            // 滑动窗口整段时间
            long windowTime = v.unit.toMillis(v.time);
            // 滑动步长
            long step = windowTime / STEP;
            // 记录本次访问的时间结点
            long currentMs = System.currentTimeMillis();
            // 过期时间点
            long removeTimeBefore = currentMs - windowTime + step;
            // 窗口向前滑动一格，删除removeTimeBefore之前的记录
            RedisUtils.zRemoveRangeByScore(k, 0, removeTimeBefore);
        });
    }

    @Override
    protected void addFrequencyControlStatisticsCount(Map<String, FrequencyControlDTO> frequencyControlMap) {
        frequencyControlMap.forEach((k,v) -> {
            // 记录本次访问的时间结点
            long currentMs = System.currentTimeMillis();
            // 添加当前访问记录
            RedisUtils.zAdd(k, String.valueOf(currentMs), currentMs);
            // 防御编程，防止某些原因，导致缓存一直存在内存中没有删除
            RedisUtils.expire(k, v.time, v.unit);
        });
    }

    @Override
    protected String getStrategyName() {
        return FrequencyStrategyType.SLIDINGWINDOW.getName();
    }
}
